#include<stdio.h>
#include<string.h>
#include<assert.h>
//1.模拟实现strlen
/*
 * 细节
 * 1:由于不对传过来的参数做修改,所以采用const修饰
 * 2.由于字符串长度是>=0的，所以返回类型为size_t
 */
size_t my_strlen1(const char* str) //方法1:计数器
{
  size_t count = 0;
  while(*str != '\0')
  {
    count++;
    str++;
  }
  return count;
}
size_t my_strlen2(const char* str) //方法2:指针-指针
{
  const char* end = str;
  while(*end!='\0')
    end++;
  return end - str;//指针-指针 :得到二者之间的元素个数,前提是二者指向同一块空间
}
size_t my_strlen3(const char* str) //方法3:递归
{ 
  if(*str != '\0')
    return 1 + my_strlen3(str+1);
  else 
    return 0;
}
//模拟实现strcpy
/*
 * 函数作用:字符串拷贝 
 * 细节:
 * 1.由于源字符串的内容不改变,所以可以使用const修饰
 * 2.要拷贝的是源字符串到\0的内容,\0也要拷贝！
 * 3.关于 *dest++  ++的优先级比*高,但是由于是后置++,所以先使用dest的内容,然后再++
 */ 
char* my_strcpy(char* dest,const char* src)
{
  assert(dest && src);//防止空指针
  char* ret = dest;//返回目的空间的起始地址
  //写法1:
//   while(*src) //遍历src一个字符一个字符的拷贝
//   {
//     *dest++ = *src++;
//   }
//   //上述只是把\0之前的内容拷贝到dest,还需要拷贝\0
//   *dest = *src;
  //写法2:
  while(*dest++ = *src++) ;//这样\0也会被拷贝
  return ret;
}
//模拟实现strcat
char* my_strcat(char* dest,const char* src)
{
  assert(dest && src);
  char* ret = dest;
  while(*dest)
    dest++;
  //先找到目标空间的\0位置,然后从该位置往后追加
  while(*dest++ = *src++);
  return ret;
}
int my_strcmp(const char* s1,const char* s2)
{
  assert(s1&&s2);
  //写法1:
  //while(*s1 == *s2) //s1和s2指向的字符相同->那就一直往后走,找到二者指向不相同的字符才跳出
  //{
  //    if(*s1 == '\0')
  //      return 0;//如果其中一个字符是\0了,说明两个字符就是相同的
  //    s1++,s2++;
  //}
  //int res =  *s1 - *s2;//本质是ascii码值相减
  //return res == 0 ?0 :res > 0 ? 1 :-1;
  
  //写法2:如果s1和s2指向的字符相同并且他们两个都不是\0,就一直往后走,直到任意一个指向\0 或者找到不相同的字符 或者 两个字符串相同,跳出循环
  while((*s1 == *s2) && *s1 && *s2)
    s1++,s2++;
  int res =  *s1 - *s2;
  return res == 0 ?0 :res > 0 ? 1 :-1;
}
//查找子串  返回子串在主串当中第一次出现的位置
const char* my_strstr(const char* str1,const char* str2)
{
  assert(str1 && str2);
  if(my_strcmp(str2,"") == 0) //字符串比较不能用== 要用strcmp
      return str1; //库中规定,如果子串为空字符串,返回主串的起始地址
  const char* start = str1;//每次遍历的起始位置
  const char* s1 = NULL,*s2 = NULL; //用于遍历主串和子串进行比较
  while(*start != '\0')
  {
    s1 = start; //s1从start位置往后遍历主串
    s2 = str2;//子串回到起始位置遍历
    while(*s1 == *s2 && *s1 && *s2) //二者相等 && 二者都没有碰到\0, 就一直往后比较
      s1++,s2++;
    if(*s2 == '\0') //子串先走到\0,说明从start位置开始匹配能找到该子串,start位置就是s2第一次出现的位置
      return start;
    else  
      start++; //从下一个位置开始匹配
   }
  return NULL;//在主串当中找不到该子串
}
int my_strncmp(const char* str1,const char* str2,size_t count)
{
  assert(str1 && str2); 
  //逐字节比较,共比较count个字节
  
  //err版本:  原因:要比较的是\0之前的字符,如果在二者相等的时候同时遇到\0,那么就返回0
  //比如: "ab\0c\0" "ab\0cdd"  此时比较前3个字节,在下面的情况就会出错了,本质应该返回0
  //while(count--)
  //{
  //  if(*str1 == *str2)
  //    str1++,str2++;
  //  else 
  //  {
  //    //解引用比较当前字节谁大
  //    if(*str1 > *str2)
  //      return 1;
  //    else  //这里只能是<0  因为如果全部字节都相同,最后会跳出循环
  //      return -1;
  //  }
  //}
  //return 0; //说明前count个字节都相同
  //写法2:
  //while(count--)
  //{
  //  if(*str1 == *str2)
  //  {
  //    if(*str1 == '\0')
  //      return 0;//如果提前遇到\0,且二者是相同的,那么就返回0
  //    str1++,str2++;
  //  }
  //  else  //找到第一个它们不相同的字符 -> 跳出循环
  //    break;
  //}
  //return *str1 - *str2;
  
  //写法3:简写版
  //只要还没比较完 && 二者指向的字符相同&&二者都没有指向\0,那么就需要一直往后找
  //直到找到二者指向的第一个不相同的字符 || 二者的前count个字节都相同
  while(count-- && (*str1 == *str2) && *str1 && *str2)
    str1++,str2++;
  return *str1 - *str2;
}
char* my_strncat(char* dest,const char* src,size_t count)
{
  assert(dest && src);
  char* ret = dest;
  while(*dest != '\0')
    dest++;
  //找到dest的\0位置开始往后追加count个字符
  //错误版本:  有可能count大于src字符串长度,如果选用下面的做法就会导致越界访问
  //while(count--)
  //{
  //  //修改：在此处加一个判断条件:if(*src != '\0')
  //  *dest++ = *src++;
  //}
  
  //正确写法：要拷贝count个字符,如果提前遇到src的\0就结束
  while(count-- && (*dest++ = *src++)) ;
  //注意要在最后位置补\0！！！
  //例如:dest:Mango  src:abcd  count = 2  ==> Mangoab\0 
  *dest = '\0';
  return ret;
}
char* my_strncpy(char* dest,const char* src,size_t count)
{
  assert(dest && src);
  char* ret = dest;
  while(count-- && (*dest++ = *src++)); 
  //注意:可能出现count>src字符串长度,所以需要往后继续补\0
  while(count > 0)
    *dest++ = '\0',count--;
  return ret;
}

int main()
{
  const char* str = "hello";
  // char buf1[64] = "Mango";
  // char buf2[64] = "Mango";
  // const char* src = "hello world";
  // printf("%s\n",strcat(buf1,src));
  // printf("%s\n",my_strcat(buf2,src));
  //printf("%s\n",my_strstr("abcdbedf","b"));
  printf("%d\n",my_strcmp("aba","ab"));
  printf("%d\n",my_strcmp("abaa","abaa"));
  printf("%d\n",my_strcmp("","a"));
  return 0;
}
